package com.ts.api.module.api.convert.core.context;

import com.gitee.apanlh.spring.BeanContextUtils;
import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.func.FuncFind;
import com.gitee.apanlh.util.reflection.ReflectionUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import com.ts.api.module.api.convert.core.annotation.ApiFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 *  API转换器注册上下文
 *
 *  @author Pan
 */
public abstract class ApiConvertRegister<V, A extends Annotation> {

    /** 缓存 */
    private Map<String, V> handlerContextCache;
    /** 上下文 */
    private ApplicationContext applicationContext;
    /** 处理器类型 */
    private Class<V> handlerType;
    /** 注解类型 */
    private Class<A> annotationType;
    /** 过滤器 */
    private FuncFind<A> annotationFilter;
    /** 唯一id集合 */
    private List<String> uniqueKeys;

    /**
     *  自定义注册默认构造函数
     */
    public ApiConvertRegister() {
        super();
        this.handlerContextCache = MapUtils.newHashMap();
        this.applicationContext = BeanContextUtils.getApplicationContext();
        this.uniqueKeys = CollUtils.newArrayList();
        registerHandler(this.applicationContext, this.handlerContextCache);
    }

    /**
     *  自定义注册默认构造函数
     */
    public ApiConvertRegister(Class<V> handlerType, Class<A> annotationType) {
        super();
        this.handlerContextCache = MapUtils.newHashMap();
        this.applicationContext = BeanContextUtils.getApplicationContext();
        this.handlerType = handlerType;
        this.annotationType = annotationType;
        this.uniqueKeys = CollUtils.newArrayList();
        registerHandler(this.applicationContext, this.handlerContextCache);
    }

    /**
     *  自动注册处理器构造函数
     */
    public ApiConvertRegister(Class<V> handlerType, Class<A> annotationType, FuncFind<A> annotationFilter) {
        super();
        this.handlerContextCache = MapUtils.newHashMap();
        this.applicationContext = BeanContextUtils.getApplicationContext();
        this.handlerType = handlerType;
        this.annotationType = annotationType;
        this.annotationFilter = annotationFilter;
        this.uniqueKeys = CollUtils.newArrayList();
        autoRegisterHandler();
    }

    /**
     * 自定义注册处理器
     *
     * @param applicationContext    上下文
     * @param contextCache          上下文缓存
     */
    public void registerHandler(ApplicationContext applicationContext, Map<String, V> contextCache) {
        throw new AbstractMethodError("未实现自定义registerHandler方法");
    }

    /**
     *  自动注册处理器
     */
    private void autoRegisterHandler() {
        autoClassRegister();
        autoFactoryRegister();
    }

    /**
     *  类级别注册
     */
    private void autoClassRegister() {
        Map<String, Object> classAnnotatedBeans = applicationContext.getBeansWithAnnotation(this.annotationType);

        IteratorUtils.entrySet(classAnnotatedBeans, (beanName, bean) -> {
            if (!this.handlerType.isInstance(bean)) {
                return ;
            }

            A annotation = AnnotationUtils.findAnnotation(bean.getClass(), this.annotationType);
            if (annotation == null || !this.annotationFilter.accept(annotation)) {
                return ;
            }
            String key = extractKey(annotation);
            putHandler(key, (V) bean);
            this.uniqueKeys.add(key);
        });
    }

    /**
     *  工厂级别注册
     */
    private void autoFactoryRegister() {
        Map<String, Object> factoryAnnotatedBeans = applicationContext.getBeansWithAnnotation(ApiFactory.class);

        IteratorUtils.entrySet(factoryAnnotatedBeans, (beanName, bean) -> {
            List<Method> listMethodAnnotation = ReflectionUtils.getMethodAnnotation(bean.getClass(), this.annotationType);
            if (ValidParam.isEmpty(listMethodAnnotation)) {
                return ;
            }

            for (Method method : listMethodAnnotation) {
                A methodAnnotation = AnnotationUtils.findAnnotation(method, this.annotationType);
                if (ValidParam.isNull(methodAnnotation) || !this.annotationFilter.accept(methodAnnotation)) {
                    continue;
                }

                try {
                    ReflectionUtils.setAccessible(method);
                    V handler = (V) method.invoke(bean);

                    String key = extractKey(methodAnnotation);
                    putHandler(key, handler);
                    this.uniqueKeys.add(key);
                } catch (Exception e) {
                    throw new IllegalStateException("无法从工厂方法获取Bean: " + method.getName(), e);
                }
            }
        });
    }

    /**
     *  类级别注册
     *
     *  @param  preExtractKey   自定义前缀抽取键
     */
    public void classRegister(String preExtractKey) {
        Map<String, Object> classAnnotatedBeans = applicationContext.getBeansWithAnnotation(this.annotationType);

        IteratorUtils.entrySet(classAnnotatedBeans, (beanName, bean) -> {
            if (!this.handlerType.isInstance(bean)) {
                return ;
            }

            A annotation = AnnotationUtils.findAnnotation(bean.getClass(), this.annotationType);
            if (annotation == null) {
                return ;
            }

            String key = extractKey(annotation);
            putHandler(preExtractKey + key, (V) bean);
            this.uniqueKeys.add(key);
        });
    }

    /**
     *  类级别注册
     *
     *  @param  preExtractKey       自定义前缀抽取键
     *  @param  annotationFilter    指定类型
     */
    public void classRegister(String preExtractKey, FuncFind<A> annotationFilter) {
        Map<String, Object> classAnnotatedBeans = applicationContext.getBeansWithAnnotation(this.annotationType);

        IteratorUtils.entrySet(classAnnotatedBeans, (beanName, bean) -> {
            if (!this.handlerType.isInstance(bean)) {
                return ;
            }

            A annotation = AnnotationUtils.findAnnotation(bean.getClass(), this.annotationType);
            if (annotation == null || !annotationFilter.accept(annotation)) {
                return ;
            }

            String key = extractKey(annotation);
            putHandler(preExtractKey + key, (V) bean);
            this.uniqueKeys.add(key);
        });
    }

    /**
     *  工厂级别注册
     *
     *  @param  preExtractKey   自定义前缀抽取键
     */
    public void factoryRegister(String preExtractKey) {
        Map<String, Object> factoryAnnotatedBeans = applicationContext.getBeansWithAnnotation(ApiFactory.class);

        IteratorUtils.entrySet(factoryAnnotatedBeans, (beanName, bean) -> {
            List<Method> listMethodAnnotation = ReflectionUtils.getMethodAnnotation(bean.getClass(), this.annotationType);
            if (ValidParam.isEmpty(listMethodAnnotation)) {
                return ;
            }

            for (Method method : listMethodAnnotation) {
                A methodAnnotation = AnnotationUtils.findAnnotation(method, this.annotationType);
                if (methodAnnotation == null) {
                    continue;
                }

                try {
                    ReflectionUtils.setAccessible(method);

                    V handler = (V) method.invoke(bean);
                    String key = extractKey(methodAnnotation);
                    putHandler(preExtractKey + key, handler);
                    this.uniqueKeys.add(key);
                } catch (Exception e) {
                    throw new IllegalStateException("无法从工厂方法获取Bean: " + method.getName(), e);
                }
            }
        });
    }

    /**
     *  工厂级别注册
     *
     *  @param  preExtractKey       自定义前缀抽取键
     *  @param  annotationFilter    指定类型
     */
    public void factoryRegister(String preExtractKey, FuncFind<A> annotationFilter) {
        Map<String, Object> factoryAnnotatedBeans = applicationContext.getBeansWithAnnotation(ApiFactory.class);

        IteratorUtils.entrySet(factoryAnnotatedBeans, (beanName, bean) -> {
            List<Method> listMethodAnnotation = ReflectionUtils.getMethodAnnotation(bean.getClass(), this.annotationType);
            if (ValidParam.isEmpty(listMethodAnnotation)) {
                return ;
            }

            for (Method method : listMethodAnnotation) {
                A methodAnnotation = AnnotationUtils.findAnnotation(method, this.annotationType);
                if (methodAnnotation == null || !annotationFilter.accept(methodAnnotation)) {
                    continue;
                }

                try {
                    ReflectionUtils.setAccessible(method);

                    V handler = (V) method.invoke(bean);
                    String key = extractKey(methodAnnotation);
                    putHandler(preExtractKey + key, handler);
                    this.uniqueKeys.add(key);
                } catch (Exception e) {
                    throw new IllegalStateException("无法从工厂方法获取Bean: " + method.getName(), e);
                }
            }
        });
    }

    /**
     *  抽取Key键
     *
     *  @param  annotation  注解
     *  @return String
     */
    public String extractKey(A annotation) {
        throw new AbstractMethodError("未实现extractKey方法,无法完成自动注入");
    }

    /**
     *  抽取Key主键
     *
     *  @param  key  键
     *  @return V
     */
    public V getHandler(String key) {
        return this.handlerContextCache.get(key);
    }

    /**
     *  获取处理上下文对象
     *
     *  @return Cache
     */
    public Map<String, V> getHandlers() {
        return this.handlerContextCache;
    }

    /**
     *  注册默认上下文
     *
     *  @param key        键
     *  @param handler    处理器
     */
    public void putHandler(String key, V handler) {
        this.handlerContextCache.put(key, handler);
    }

    /**
     *  获取默认拦截器
     *
     *  @return V
     */
    public V getDefaultHandler() {
        throw new AbstractMethodError("未实现extractKey方法,无法完成自动注入");
    }

    /**
     *  获取存储的所以唯一建
     *
     *  @return List
     */
    public List<String> getUniqueKeys() {
        return this.uniqueKeys;
    }
}
